/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2009 INRIA
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation;
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Author: Guillaume Seguin <guillaume@segu.in>
 */

#include "multithreading-helper.h"

#include "ns3/multithreaded-simulator-impl.h"
#include "ns3/multithreading-partition.h"
#include "ns3/mpi-interface.h"
#include "ns3/simulator.h"
#include "ns3/node-list.h"
#include "ns3/net-device.h"
#include "ns3/channel.h"

#include <algorithm>

#include "ns3/csma-channel.h"
#include "ns3/point-to-point-channel.h"
#include "ns3/point-to-point-net-device.h"

NS_LOG_COMPONENT_DEFINE ("MultiThreadingHelper");

namespace ns3 {

MultiThreadingHelper::MultiThreadingHelper ()
  : m_enableMultiThreading (false),
    m_simulator (0)
{}

void
MultiThreadingHelper::Enable ()
{
  m_enableMultiThreading = true;
  m_simulator = CreateObject<MultiThreadedSimulatorImpl> ();
  Simulator::SetImplementation (m_simulator);
}

void
MultiThreadingHelper::Install () const
{
  if (m_enableMultiThreading)
    {
      pthread_mutexattr_t lock_attr;
      pthread_mutexattr_init (&lock_attr);
      for (NodeList::Iterator i = NodeList::Begin (); i != NodeList::End (); ++i)
        {
					Ptr<Node> node = (*i);
					uint32_t systemId = node->GetSystemId ();
					if(systemId == MpiInterface::GetSystemId ())
						{
							MultiThreadingPartition *partition = new MultiThreadingPartition;
							partition->id = node->GetId ();
							partition->rank = systemId;
							partition->nMessages = 0;
							partition->events = 0;
							partition->currentUid = 0;
							// uids are allocated from 4.
							// uid 0 is "invalid" events
							// uid 1 is "now" events
							// uid 2 is "destroy" events
							partition->uid = 4;
							partition->currentTs = 0;
							partition->minDelay = GetMinDelay (node);
							pthread_mutex_init (&partition->eventMessagesLock, &lock_attr);
							m_simulator->AddPartition (partition->id, partition);
						}
        }
    }
}

uint64_t
MultiThreadingHelper::GetMinDelay (Ptr<Node> node) const
{
  int srcDevCount = node->GetNDevices ();
  uint64_t minDelay = Simulator::GetMaximumSimulationTime ().GetTimeStep ();
  for (int srcDevIndex = 0; srcDevIndex < srcDevCount; srcDevIndex++)
    {
      Ptr<NetDevice> srcDevice = node->GetDevice (srcDevIndex);
      Ptr<Channel> channel = srcDevice->GetChannel ();
      if (channel == 0)
        {
          continue;
        }
    
      uint64_t delay = channel->GetDelay ().GetTimeStep ();
      /*
      uint8_t type = channel->GetType();
      if (2 == type) 
        {
          //std::cout<<"==============type 2"<<std::endl;
          Ptr<CsmaChannel> cschannel = DynamicCast<CsmaChannel, Channel>(channel);
          delay = cschannel->GetDelay ().GetTimeStep ();
        }
      else if(1 == type)
        {
          //std::cout<<"===========type 1"<<std::endl;
          Ptr<PointToPointChannel> p2pchannel = DynamicCast<PointToPointChannel,Channel>(channel);
          delay = p2pchannel->GetDelay ().GetTimeStep ();
        }
      else 
        delay = channel->GetDelay ().GetTimeStep ();
      //std::cout<<"+++++++++++++++delay is "<<delay<<std::endl;
      */

      //uint64_t delay = channel->GetMinDelay (srcDevice).GetTimeStep ();
      std::cout<<"+++++++++++++++delay is "<<delay<<std::endl;
      //NS_ASSERT (delay != 0);
      minDelay = std::min (minDelay, delay);
    }
  return minDelay;
}

uint32_t
MultiThreadingHelper::AddSubPartition(uint32_t context, uint32_t subContext)
{
	uint32_t mappedContext = m_simulator->AddSubPartition(context, subContext);
	return mappedContext;
}

} // namespace ns3
